package com.kexio.core.security.filter;

import com.kexio.common.dto.Result;
import com.kexio.common.util.JsonUtils;
import com.kexio.core.security.context.UserContext;
import com.kexio.core.security.jwt.JwtTokenValidator;
import com.kexio.core.security.service.UserContextService;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;

/**
 * 安全过滤器
 * 负责JWT令牌验证和用户上下文初始化
 *
 * @author Kexio Team
 * @since 1.0.0
 */
public class SecurityFilter implements Filter {

    private static final Logger log = LoggerFactory.getLogger(SecurityFilter.class);

    /**
     * 不需要认证的路径
     */
    private static final List<String> EXCLUDE_PATHS = Arrays.asList(
            "/api/auth/login",
            "/api/auth/register", 
            "/api/auth/refresh",
            "/api/health",
            "/api/info",
            "/favicon.ico",
            "/swagger-ui",
            "/v3/api-docs",
            "/actuator",
            "/h2-console"
    );

    private final JwtTokenValidator jwtTokenValidator;
    private final UserContextService userContextService;

    public SecurityFilter(JwtTokenValidator jwtTokenValidator, UserContextService userContextService) {
        this.jwtTokenValidator = jwtTokenValidator;
        this.userContextService = userContextService;
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("==> Kexio Security Filter 初始化完成");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        String requestURI = httpRequest.getRequestURI();
        String method = httpRequest.getMethod();

        // 记录请求日志
        log.debug("处理请求: {} {}", method, requestURI);

        try {
            // 检查是否为不需要认证的路径
            if (isExcludePath(requestURI)) {
                log.debug("跳过认证检查: {}", requestURI);
                chain.doFilter(request, response);
                return;
            }

            // 获取Authorization头
            String authHeader = httpRequest.getHeader("Authorization");
            if (authHeader == null || !authHeader.startsWith("Bearer ")) {
                log.debug("缺少或无效的Authorization头: {}", requestURI);
                sendUnauthorizedResponse(httpResponse, "缺少访问令牌");
                return;
            }

            // 提取JWT令牌
            String accessToken = authHeader.substring(7);
            
            // 验证令牌并提取用户信息
            UserContext userContext = jwtTokenValidator.validateAndExtract(accessToken);
            if (userContext == null) {
                log.debug("无效的访问令牌: {}", requestURI);
                sendUnauthorizedResponse(httpResponse, "无效的访问令牌");
                return;
            }

            // 检查令牌是否即将过期
            if (jwtTokenValidator.isTokenExpiringSoon(accessToken)) {
                long remainingTime = jwtTokenValidator.getRemainingTime(accessToken);
                log.info("用户 {} 的访问令牌即将过期，剩余时间: {} 秒", 
                        userContext.getUsername(), remainingTime);
                
                // 在响应头中添加令牌即将过期的提示
                httpResponse.setHeader("X-Token-Expire-Soon", "true");
                httpResponse.setHeader("X-Token-Remaining-Time", String.valueOf(remainingTime));
            }

            // 设置用户上下文
            userContextService.updateUserContext(userContext);
            
            // 在请求属性中设置用户信息，方便后续使用
            httpRequest.setAttribute("currentUser", userContext);
            httpRequest.setAttribute("currentUserId", userContext.getUserId());
            httpRequest.setAttribute("currentUsername", userContext.getUsername());
            httpRequest.setAttribute("currentTenantId", userContext.getTenantId());

            log.debug("用户认证成功: userId={}, username={}, tenantId={}", 
                    userContext.getUserId(), userContext.getUsername(), userContext.getTenantId());

            // 继续处理请求
            chain.doFilter(request, response);

        } catch (Exception e) {
            log.error("安全过滤器处理异常: {}", e.getMessage(), e);
            sendInternalErrorResponse(httpResponse, "认证处理失败");
        } finally {
            // 清除用户上下文(防止内存泄露)
            userContextService.clearUserContext();
        }
    }

    @Override
    public void destroy() {
        log.info("==> Kexio Security Filter 销毁完成");
    }

    /**
     * 检查是否为不需要认证的路径
     *
     * @param requestURI 请求路径
     * @return 是否为排除路径
     */
    private boolean isExcludePath(String requestURI) {
        return EXCLUDE_PATHS.stream().anyMatch(excludePath -> {
            if (excludePath.endsWith("*")) {
                return requestURI.startsWith(excludePath.substring(0, excludePath.length() - 1));
            } else {
                return requestURI.equals(excludePath) || requestURI.startsWith(excludePath);
            }
        });
    }

    /**
     * 发送未授权响应
     *
     * @param response HttpServletResponse
     * @param message  错误消息
     * @throws IOException IO异常
     */
    private void sendUnauthorizedResponse(HttpServletResponse response, String message) throws IOException {
        sendErrorResponse(response, HttpStatus.UNAUTHORIZED.value(), message);
    }

    /**
     * 发送内部服务器错误响应
     *
     * @param response HttpServletResponse
     * @param message  错误消息
     * @throws IOException IO异常
     */
    private void sendInternalErrorResponse(HttpServletResponse response, String message) throws IOException {
        sendErrorResponse(response, HttpStatus.INTERNAL_SERVER_ERROR.value(), message);
    }

    /**
     * 发送错误响应
     *
     * @param response HttpServletResponse
     * @param status   状态码
     * @param message  错误消息
     * @throws IOException IO异常
     */
    private void sendErrorResponse(HttpServletResponse response, int status, String message) throws IOException {
        response.setStatus(status);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");

        Result<Void> result = Result.error(status, message);
        String jsonResponse = JsonUtils.toJson(result);

        try (PrintWriter writer = response.getWriter()) {
            writer.write(jsonResponse);
            writer.flush();
        }
    }
}
